class BezierClass {
    constructor(bezierCtrlNodesArr, cnt = 100) {
        this.bezierArr = []
        this.bezierCtrlNodesArr = bezierCtrlNodesArr
        this.cnt = cnt
    }

    factorial(num) {
        if (num <= 1) {
            return 1;
        } else {
            return num * this.factorial(num - 1);
        }
    }

    bezier(t) {
        var x = 0,
            y = 0,
            bezierCtrlNodesArr = this.bezierCtrlNodesArr,
            n = bezierCtrlNodesArr.length - 1,
            self = this
        bezierCtrlNodesArr.forEach(function (item, index) {
            if (!index) {
                x += item[0] * Math.pow((1 - t), n - index) * Math.pow(t, index)
                y += item[1] * Math.pow((1 - t), n - index) * Math.pow(t, index)
            } else {
                x += self.factorial(n) / self.factorial(index) / self.factorial(n - index) * item[0] * Math.pow((1 - t), n - index) * Math.pow(t, index)
                y += self.factorial(n) / self.factorial(index) / self.factorial(n - index) * item[1] * Math.pow((1 - t), n - index) * Math.pow(t, index)
            }
        })
        return {
            x: x,
            y: y
        }
    }

    pointGet() {
        const step = 1 / this.cnt
        for (let i = 0; i < 1; i += step) {
            this.bezierArr.push(this.bezier(i))
        }
        this.bezierArr.push(this.bezierCtrlNodesArr[this.bezierCtrlNodesArr.length - 1])
        return this.bezierArr
    }

    curXyTrans(pos, start, end) {
        const { y } = this.bezier(pos);
        const maxY = this.bezierCtrlNodesArr[this.bezierCtrlNodesArr.length - 1][1]
        const transY = y / maxY;
        const diff = end - start;
        return start + diff * transY;
    }

}


class MyAnimation {

    constructor(time) {

        this.time = time

        this.easingMethod = {
            Linear: {
                InOut(pos) {
                    if ((pos /= 0.5) < 1) return 0.5 * Math.pow(pos, 2);
                    return - 0.5 * ((pos -= 2) * pos - 2);
                },
            },
            Bezier: {
                InOut: (() => {
                    const point = [
                        [1, 1], [92, 129], [219, 197], [388, 236], [700, 259], [2423, 260]
                    ];
                    const b = new BezierClass(point);
                    return (pos, start, end) => b.curXyTrans(pos, start, end);
                })(),
            }
        }
    }

    animate(start, end, callback, endCallback, easing = "Linear.InOut") {
        const self = this;
        let startTime = performance.now();
        let diff = {};
        for (let i in start) {
            diff[i] = end[i] - start[i];
        }
        function loop() {
            raf = requestAnimationFrame(loop);
            const passTime = performance.now() - startTime;
            let per = passTime / self.time;
            if (per >= 1) {
                per = 1;
                cancelAnimationFrame(raf);
                endCallback && endCallback()
            }
            let pass = {};
            for (let i in diff) {
                if (easing.includes('Bezier')) {
                    pass[i] = self['easingFn'](easing).call(self, per, start[i], end[i]);
                    continue;
                }
                pass[i] = start[i] + diff[i] * self['easingFn'](easing).call(self, per);
            }
            callback(pass);
        }
        let raf = requestAnimationFrame(loop);
    }

    easingFn(easing) {
        const easingKey = easing.split('.')

        let easingFn = this.easingMethod
        for (let i = 0; i < easingKey.length; i++) {
            easingFn = easingFn[easingKey[i]]
        }
        return easingFn
    }
}


export default MyAnimation;